#include "stdafx.h"
#include "Registry.h"


namespace Logic
{
	namespace Util
	{

CBdRegKey::CBdRegKey()
: m_Key(NULL), m_hWatchEvent(0) 
{

}

CBdRegKey::CBdRegKey(HKEY rootkey, const CStringW & strSubkey, REGSAM access)
: m_Key(NULL), m_hWatchEvent(0)
{
	if (rootkey) 
	{
		if (access & (KEY_SET_VALUE | KEY_CREATE_SUB_KEY | KEY_CREATE_LINK))
			Create(rootkey, strSubkey, access);
		else
			Open(rootkey, strSubkey, access);
	}
}

CBdRegKey::~CBdRegKey()
{
	Close();
}

VOID CBdRegKey::Close() 
{
	StopWatching();

	if (m_Key) 
	{
		::RegCloseKey(m_Key);
		m_Key = NULL;
	}
}

BOOL CBdRegKey::Create(HKEY rootkey, const CStringW & strSubkey, REGSAM access) 
{
	DWORD dwDispositionValue;
	return CreateWithDisposition(rootkey, strSubkey, &dwDispositionValue, access);
}

BOOL CBdRegKey::CreateWithDisposition(HKEY rootkey, const CStringW & strSubkey,
	DWORD * pdwDisposition, REGSAM access) 
{
	Close();

	LONG result = RegCreateKeyEx(rootkey,
	   strSubkey,
	   0,
	   NULL,
	   REG_OPTION_NON_VOLATILE,
	   access,
	   NULL,
	   &m_Key,
	   pdwDisposition);

	if (result != ERROR_SUCCESS)
	{
	   m_Key = NULL;
	   return FALSE;
	}

	return TRUE;
}

BOOL CBdRegKey::Open(HKEY rootkey, const CStringW & strSubkey, REGSAM access) 
{
	Close();

	LONG result = RegOpenKeyEx(rootkey, strSubkey, 0, access, &m_Key);

	if (result != ERROR_SUCCESS) 
	{
		m_Key = NULL;
		return FALSE;
	}

	return TRUE;
}

BOOL CBdRegKey::CreateKey(const CStringW & strName, REGSAM access)
{
	HKEY subkey = NULL;
	LONG result = RegCreateKeyEx(m_Key, strName, 0, NULL, REG_OPTION_NON_VOLATILE,
		access, NULL, &subkey, NULL);

	Close();

	m_Key = subkey;

	return (result == ERROR_SUCCESS);
}

BOOL CBdRegKey::OpenKey(const CStringW & strName, REGSAM access) 
{
	HKEY subkey = NULL;
	LONG result = RegOpenKeyEx(m_Key, strName, 0, access, &subkey);

	Close();

	m_Key = subkey;
	return (result == ERROR_SUCCESS);
}

DWORD CBdRegKey::ValueCount() 
{
	DWORD dwCount = 0;
	HRESULT result = RegQueryInfoKey(m_Key, NULL, 0, NULL, NULL, NULL,
		NULL, &dwCount, NULL, NULL, NULL, NULL);

	return (result != ERROR_SUCCESS) ? 0 : dwCount;
}

BOOL CBdRegKey::ReadName(INT nIndex, CStringW & strName) 
{
	WCHAR szBuf[256];
	DWORD dwBufsize = 256;
	LRESULT r = ::RegEnumValue(m_Key, nIndex, szBuf, &dwBufsize, NULL, NULL,
		NULL, NULL);

	if (r != ERROR_SUCCESS)
		return FALSE;

	strName = szBuf;

	return TRUE;
}

BOOL CBdRegKey::ValueExists(const CStringW & strName)
{
	if (!m_Key)
		return FALSE;

	HRESULT result = RegQueryValueEx(m_Key, strName, 0, NULL, NULL, NULL);
	return (result == ERROR_SUCCESS);
}

BOOL CBdRegKey::ReadValue(const CStringW & strName, VOID * pData,
	DWORD * pdwSize, DWORD * pdwType) 
{
	if (!m_Key)
	   return FALSE;

	HRESULT result = RegQueryValueEx(m_Key, strName, 0, pdwType,
	   reinterpret_cast<LPBYTE>(pData), pdwSize);

	return (result == ERROR_SUCCESS);
}

BOOL CBdRegKey::ReadValue(const CStringW & strName, CStringW & strValue) 
{
	const size_t kMaxStringLength = 1024;  // This is after expansion.
	// Use the one of the other forms of ReadValue if 1024 is too small for you.
	WCHAR szRawValue[kMaxStringLength];
	memset((VOID *)szRawValue, 0, sizeof(szRawValue));

	DWORD dwType = REG_SZ, dwSize = sizeof(szRawValue);
	if (ReadValue(strName, szRawValue, &dwSize, &dwType))
	{
		if (dwType == REG_SZ)
		{
			strValue = szRawValue;
		} 
		else if (dwType == REG_EXPAND_SZ)
		{
			WCHAR szExpanded[kMaxStringLength];
			dwSize = ExpandEnvironmentStrings(szRawValue, szExpanded, kMaxStringLength);
			// Success: returns the number of wchar_t's copied
			// Fail: buffer too small, returns the size required
			// Fail: other, returns 0
			if (dwSize == 0 || dwSize > kMaxStringLength)
				return FALSE;

			strValue = szExpanded;
		}
		else 
		{
			// Not a string. Oops.
			return FALSE;
		}

		return TRUE;
	}

	return FALSE;
}

BOOL CBdRegKey::ReadValueDW(const CStringW & strName, DWORD * pdwValue) 
{
	DWORD dwType = REG_DWORD;
	DWORD dwSize = sizeof(DWORD);
	DWORD dwResult = 0;
	if (ReadValue(strName, &dwResult, &dwSize, &dwType) &&
		(dwType == REG_DWORD || dwType == REG_BINARY) &&
		dwSize == sizeof(DWORD))
	{
		*pdwValue = dwResult;
		return TRUE;
	}

	return FALSE;
}

BOOL CBdRegKey::WriteValue(const CStringW & strName, const VOID * pData,
	DWORD dwSize, DWORD dwType)
{
	if (!m_Key)
		return FALSE;

	HRESULT result = RegSetValueEx(
		m_Key,
		strName,
		0,
		dwType,
		reinterpret_cast<LPBYTE>(const_cast<VOID*>(pData)),
		dwSize);

	return (result == ERROR_SUCCESS);
}

BOOL CBdRegKey::WriteValue(const CStringW & strName, const CStringW & strValue)
{
	return WriteValue(strName, strValue,
		static_cast<DWORD>(sizeof(WCHAR) * strValue.GetLength()), REG_SZ);
}

BOOL CBdRegKey::WriteValue(const CStringW & strName, DWORD dwValue) 
{
	return WriteValue(strName, &dwValue,
		static_cast<DWORD>(sizeof(dwValue)), REG_DWORD);
}

BOOL CBdRegKey::DeleteKey(const CStringW & strName)
{
	return (!m_Key) ? FALSE : (ERROR_SUCCESS == SHDeleteKey(m_Key, strName));
}

BOOL CBdRegKey::DeleteValue(const CStringW & strValueName)
{
	HRESULT result = RegDeleteValue(m_Key, strValueName);

	return (result == ERROR_SUCCESS);
}

BOOL CBdRegKey::StartWatching()
{
	if (!m_hWatchEvent)
		m_hWatchEvent = CreateEvent(NULL, TRUE, FALSE, NULL);

	DWORD filter = REG_NOTIFY_CHANGE_NAME |
		REG_NOTIFY_CHANGE_ATTRIBUTES |
		REG_NOTIFY_CHANGE_LAST_SET |
		REG_NOTIFY_CHANGE_SECURITY;

	// Watch the registry key for a change of value.
	HRESULT result = RegNotifyChangeKeyValue(m_Key, TRUE, filter,
		m_hWatchEvent, TRUE);

	if (SUCCEEDED(result))
	{
		return TRUE;
	}
	else
	{
		CloseHandle(m_hWatchEvent);
		m_hWatchEvent = 0;
		return FALSE;
	}
}

BOOL CBdRegKey::StopWatching()
{
	if (m_hWatchEvent)
	{
		CloseHandle(m_hWatchEvent);
		m_hWatchEvent = 0;
		return TRUE;
	}
	return FALSE;
}

BOOL CBdRegKey::HasChanged()
{
	if (m_hWatchEvent)
	{
		if (WaitForSingleObject(m_hWatchEvent, 0) == WAIT_OBJECT_0) 
		{
			StartWatching();
			return TRUE;
		}
	}

	return FALSE;
}
	}
}